package com.ygp.dtp.core.thread;

import com.alibaba.ttl.threadpool.TtlExecutors;
import com.ygp.dtp.common.constant.DynamicTpConstant;
import com.ygp.dtp.common.dto.NotifyItem;
import com.ygp.dtp.common.enums.QueueTypeEnum;
import com.ygp.dtp.common.queue.VariableLinkedBlockingQueue;
import com.ygp.dtp.core.reject.factory.RejectedCountableFactory;
import com.ygp.dtp.core.reject.handler.RejectedCountableCallerRunsPolicy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * @description: 用于优雅地创建 ThreadPoolExecutor 的构建器。
 * @motto: 代码源于生活，高于生活艺术
 * @author: zhouhengzhe
 * @date: 2022/8/9 16:42
 * @since 1.0
 **/
@Component
public class ThreadPoolBuilder {

    @Resource
    private RejectedCountableFactory rejectedCountableFactory;

    /**
     * 动态线程池的名称。
     */
    private String threadPoolName = "DynamicTp";

    /**
     * 线程池中的常驻核心线程数
     */
    private int corePoolSize = 1;

    /**
     * 线程池中能够容纳同时执行的最大线程数，此值必须>=1
     */
    private int maximumPoolSize = DynamicTpConstant.AVAILABLE_PROCESSORS;

    /**
     * 多余的空闲线程的存活时间
     * 当前线程池数量超过corePoolSize时，当空闲时间达到keepAliveTime时，多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
     */
    private long keepAliveTime = 30;

    /**
     * keepAliveTime的单位
     */
    private TimeUnit timeUnit = TimeUnit.SECONDS;

    /**
     * 任务队列，被提交但是尚未被执行的任务,{@link com.ygp.dtp.common.enums.QueueTypeEnum}
     */
    private BlockingQueue<Runnable> workQueue = new VariableLinkedBlockingQueue<>(1024);

    /**
     * 拒绝策略，表示当队列满了并且工作线程-大于等于线程池的数量最大线程数（maxinumPoolSize）时如何来拒绝请求执行的runnable的策略。
     * <p>
     * 一般我们创建线程池时，为防止资源被耗尽，任务队列都会选择创建有界任务队列，但种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时，
     * 这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略，来处理线程池"超载"的情况。ThreadPoolExecutor自带的拒绝策略如下：
     * 1、AbortPolicy策略：该策略会直接抛出异常，阻止系统正常工作；
     * <p>
     * 2、CallerRunsPolicy策略：如果线程池的线程数量达到上限，该策略会把任务队列中的任务放在调用者线程当中运行；
     * <p>
     * 3、DiscardOledestPolicy策略：该策略会丢弃任务队列中最老的一个任务，也就是当前任务队列中最先被添加进去的，马上要被执行的那个任务，并尝试再次提交；
     * <p>
     * 4、DiscardPolicy策略：该策略会默默丢弃无法处理的任务，不予任何处理。当然使用此策略，业务场景中需允许任务的丢失；
     */
    private RejectedExecutionHandler rejectedExecutionHandler = new RejectedCountableCallerRunsPolicy();

    /**
     * 线程工厂，用于创建线程，一般用默认即可；
     */
    private ThreadFactory threadFactory = new NamedThreadFactory(DynamicTpConstant.FACTORY_NAME);
    /**
     * 是否允许核心线程超时。
     */
    private boolean allowCoreThreadTimeOut = false;

    /**
     * 动态线程池/普通
     */
    private boolean dynamic = true;

    /**
     * Notify items, see {@link  com.ygp.dtp.common.enums.NotifyTypeEnum}
     */
    private List<NotifyItem> notifyItems = NotifyItem.getDefaultNotifyItems();

    /**
     * 单例模式创建对象
     */
    private ThreadPoolBuilder() {

    }

    /**
     * 构建者模式
     *
     * @return
     */
    public static ThreadPoolBuilder builder() {
        return new ThreadPoolBuilder();
    }

    public ThreadPoolBuilder threadPoolName(String threadPoolName) {
        this.threadPoolName = threadPoolName;
        return this;
    }

    public ThreadPoolBuilder corePoolSize(int corePoolSize) {
        if (corePoolSize >= 0) {
            this.corePoolSize = corePoolSize;
        }
        return this;
    }

    public ThreadPoolBuilder maximumPoolSize(int maximumPoolSize) {
        if (maximumPoolSize > 0) {
            this.maximumPoolSize = maximumPoolSize;
        }
        return this;
    }

    public ThreadPoolBuilder keepAliveTime(long keepAliveTime) {
        if (keepAliveTime > 0) {
            this.keepAliveTime = keepAliveTime;
        }
        return this;
    }

    public ThreadPoolBuilder timeUnit(TimeUnit timeUnit) {
        if (Objects.nonNull(timeUnit)) {
            this.timeUnit = timeUnit;
        }
        return this;
    }

    public ThreadPoolBuilder workQueue(String queueName, Integer capacity, Boolean fair) {
        if (StringUtils.isNotBlank(queueName)) {
            this.workQueue = QueueTypeEnum.buildBlockingQueue(queueName, Objects.nonNull(capacity) ? capacity : 1024, Objects.nonNull(fair) && fair);
        }
        return this;
    }

    public ThreadPoolBuilder rejectedExecutionHandler(String rejectedName) {
        if (StringUtils.isNotBlank(rejectedName)) {
            this.rejectedExecutionHandler = rejectedCountableFactory.getRejected(rejectedName);
        }
        return this;
    }

    public ThreadPoolBuilder threadFactory(String prefix) {
        if (StringUtils.isNotBlank(prefix)) {
            this.threadFactory = new NamedThreadFactory(prefix);
        }
        return this;
    }

    public ThreadPoolBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        return this;
    }

    public ThreadPoolBuilder dynamic(boolean dynamic) {
        this.dynamic = dynamic;
        return this;
    }

    public ThreadPoolBuilder notifyItems(List<NotifyItem> notifyItems) {
        this.notifyItems = notifyItems;
        return this;
    }

    /**
     * 根据动态字段构建
     *
     * @return
     */
    public ThreadPoolExecutor build() {
        if (dynamic) {
            return buildDynamicExecutor(this);
        } else {
            return buildCommonExecutor(this);
        }
    }

    /**
     * 构建一个动态的 ThreadPoolExecutor。
     * @return
     */
    public DynamicThreadPoolExecutor buildDynamic(){
        return buildDynamicExecutor(this);
    }

    /**
     * 构建一个简单的ThreadPoolExecutor
     * @return
     */
    public ThreadPoolExecutor buildCommon(){
        return buildCommonExecutor(this);
    }

    /**
     * 使用 ttl 构建线程池执行器和包装器
     * @return
     */
    public ExecutorService buildWithTtl(){
        return TtlExecutors.getTtlExecutorService(buildCommonExecutor(this));

    }

    /**
     * 构建动态threadPoolExecutor。
     *
     * @param threadPoolBuilder
     * @return
     */
    private DynamicThreadPoolExecutor buildDynamicExecutor(ThreadPoolBuilder threadPoolBuilder) {
        Assert.notNull(threadPoolBuilder.threadPoolName, "线程池名字必须不能为空");
        DynamicThreadPoolExecutor dynamicThreadPoolExecutor = new DynamicThreadPoolExecutor(
                threadPoolBuilder.corePoolSize,
                threadPoolBuilder.maximumPoolSize,
                threadPoolBuilder.keepAliveTime,
                threadPoolBuilder.timeUnit,
                threadPoolBuilder.workQueue,
                threadPoolBuilder.threadFactory,
                threadPoolBuilder.rejectedExecutionHandler
        );
        dynamicThreadPoolExecutor.allowCoreThreadTimeOut(threadPoolBuilder.allowCoreThreadTimeOut);
        dynamicThreadPoolExecutor.setThreadPoolName(threadPoolName);
        dynamicThreadPoolExecutor.setNotifyItems(threadPoolBuilder.notifyItems);
        return dynamicThreadPoolExecutor;
    }

    /**
     * 构建通用threadPoolExecutor，不由DynamicTp框架管理。
     *
     * @param threadPoolBuilder
     * @return
     */
    private ThreadPoolExecutor buildCommonExecutor(ThreadPoolBuilder threadPoolBuilder) {
        DynamicThreadPoolExecutor dynamicThreadPoolExecutor = new DynamicThreadPoolExecutor(
                threadPoolBuilder.corePoolSize,
                threadPoolBuilder.maximumPoolSize,
                threadPoolBuilder.keepAliveTime,
                threadPoolBuilder.timeUnit,
                threadPoolBuilder.workQueue,
                threadPoolBuilder.threadFactory,
                threadPoolBuilder.rejectedExecutionHandler
        );
        dynamicThreadPoolExecutor.allowCoreThreadTimeOut(threadPoolBuilder.allowCoreThreadTimeOut);
        return dynamicThreadPoolExecutor;
    }
}
